#include "DXUT.h"
#include "SDKmisc.h"

#include "IRender.h"

#include <string>
#include <vector>

using namespace std;

//#define SAFE_RELEASE(v) if(v) { v->release; delete v; }
using namespace Rendering;
using namespace Rendering::Layout;

#ifndef CRENDERDX9
#define CRENDERDX9

namespace Rendering
{

  class CRenderDX9 : public IRender
  {
  public:
    class BufferDX9Index;
    class BufferDX9Vertex;
  public:
    IDirect3DDevice9* pIDevice;
    /* Buffer prepare optimization: */
    // Index:
    //BufferDX9Index *PreparedIndexBuffer;
    Buffer::Allign        *IDirect3DPreparedIndexBufferAllign;
    IDirect3DIndexBuffer9 *IDirect3DPreparedIndexBuffer;
    // Vertex:
    //vector<BufferDX9Vertex *> PreparedVertexBuffers;
    vector<Buffer::Allign*>          IDirect3DPreparedVertexBuffersAllign;
    vector<IDirect3DVertexBuffer9 *> IDirect3DPreparedVertexBuffers;
    // Topology
    D3DPRIMITIVETYPE IDirect3DTopology;
  public:
    D3DPRIMITIVETYPE GetTopology(Buffer::Topology topology)
    {
      return (D3DPRIMITIVETYPE) topology;
    }
    UINT64 GetPrimitiveCount( D3DPRIMITIVETYPE topology, Buffer::Allign *pallign )
    {
      switch( topology )
      {
        case D3DPT_POINTLIST     : return pallign->Element_Size;
        case D3DPT_LINELIST      : return pallign->Element_Size / 2;
        case D3DPT_LINESTRIP     : return pallign->Element_Size - 1;
        case D3DPT_TRIANGLELIST  : return pallign->Element_Size / 3;
        case D3DPT_TRIANGLESTRIP : return pallign->Element_Size - 2;
        case D3DPT_TRIANGLEFAN   : return pallign->Element_Size - 2;
        default                  : return 0;
      }
    }  
  public:
    class IInternalPrepareble
    {
    public:
      virtual bool Prepare(UINT64 parameter_id, Buffer::Allign *pallign = NULL)=0;
    };
    class IInternalMappable
    {
    public:
      virtual bool Map(Mapping::Descriptor *pdescriptor)=0;
      virtual bool Unmap()=0;
    };
    class IBufferDX9 : public IInternalMappable, public IInternalPrepareble, public IBuffer 
    { 
    public:
      virtual bool IsSubBufferCreatable( UINT64 element_count )
      {
        return true;
      }
      virtual IBuffer* CreateSubBuffer( UINT64 element_count )=0;
    };
    class SubBuffer : public IBufferDX9
    {
      Buffer::Allign Allign;
      IBufferDX9    *pMaster;
      Buffer::Descriptor Descriptor;
      //Sub Buffers:
      vector<IBuffer*> SubBuffers;
    public:
      SubBuffer( IBufferDX9 *pmaster, Buffer::Allign &sub_allign )
      {
        pMaster = pmaster;
        Allign = sub_allign;
        Descriptor.pAllign = &Allign;
        Descriptor.pInput = pmaster->GetDescriptor().pInput;
        //
        SubBuffers.resize(0);
      }
      virtual bool  SetData( void* ptr, UINT64 size ) 
      { 
        return pMaster->SetData(ptr,size);
      }
      virtual bool  GetData( void* ptr, UINT64 size )
      { 
        return pMaster->GetData(ptr,size);
      }    
      virtual Buffer::Descriptor GetDescriptor() { return Descriptor; }
      virtual bool Prepare(UINT64 parameter_id, Buffer::Allign *pallign = NULL )
      {
        return pMaster->Prepare(parameter_id, &Allign);
      }
      virtual bool Map(Mapping::Descriptor *pDescriptor)
      {
        return pMaster->Map(pDescriptor);
      };
      virtual bool Unmap()
      {
        return pMaster->Unmap();
      };
      virtual Mapping::Status GetMappingStatus() { return pMaster->GetMappingStatus(); };
      virtual Render::Resource::Type GetType() { return Render::Resource::RT_SubBuffer; }
      UINT64 GetPrimitiveCount( D3DPRIMITIVETYPE topology )
      {
        switch( topology )
        {
          case D3DPT_POINTLIST     : return Allign.Element_End;
          case D3DPT_LINELIST      : return Allign.Element_End / 2;
          case D3DPT_LINESTRIP     : return Allign.Element_End - 1;
          case D3DPT_TRIANGLELIST  : return Allign.Element_End / 3;
          case D3DPT_TRIANGLESTRIP : return Allign.Element_End - 2;
          case D3DPT_TRIANGLEFAN   : return Allign.Element_End - 2;
          default                  : return 0;
        }
      }
      UINT64 GetPrimitiveCount( Buffer::Topology topology )
      {
        switch( topology )
        {
          case Buffer::BT_Point_List     : return Allign.Element_End;
          case Buffer::BT_Line_List      : return Allign.Element_End / 2;
          case Buffer::BT_Line_Strip     : return Allign.Element_End - 1;
          case Buffer::BT_Triangle_List  : return Allign.Element_End / 3;
          case Buffer::BT_Triangle_Strip : return Allign.Element_End - 2;
          case Buffer::BT_Triangle_Fan   : return Allign.Element_End - 2;
          default                        : return 0;
        }
      }
      virtual IBuffer* CreateSubBuffer( UINT64 element_count )
      {
        return NULL;
      }    
      virtual vector<IBuffer*>* GetSubBuffers()
      {
        return &SubBuffers;
      }
    };
    class BufferDX9Index : public IBufferDX9
    {
    public:
      BufferDX9Index(CRenderDX9 *prender, Layout::Declaration &input, UINT64 elementCount)/*:CResource(Render::Resource::RT_Buffer)*/
      {
        MapStatus = Mapping::MS_Unmapped;
        pRender = prender;
        pIDirect3DDevice  = pRender->pIDevice;

        Declaration = input;
       
        Allign.Innitialize(0, &input, elementCount);

        Layout::Declaration::Descriptor InputDescriptor = input.GetDescriptor()->at(0);
        D3DFORMAT IndexBufferFormat = Layout::LF_Word == InputDescriptor.Format ? D3DFMT_INDEX16 : Layout::LF_DWord == InputDescriptor.Format ? D3DFMT_INDEX32 : D3DFMT_UNKNOWN;
        
        SubBufferOffsetInElements = NULL;

        if( D3D_OK == prender->pIDevice->CreateIndexBuffer( Allign.Byte_Size,
                                                            0,                                        
                                                            IndexBufferFormat,
                                                            D3DPOOL_MANAGED,                          
                                                            &pIDirect3DBuffer,
                                                            0 ) )
        {
          Descriptor.pAllign = &Allign;
          Descriptor.pInput  = &Declaration;
        } 
        else
        {
          //NOT OK
          Descriptor.pAllign = NULL;
          Descriptor.pInput  = NULL;
        }
      }
      virtual bool  SetData( void* ptr, UINT64 size ) 
      { 
        void* vPool = (void*) 0;  

        if( D3D_OK == pIDirect3DBuffer->Lock(0, 0, &vPool, D3DLOCK_NOSYSLOCK ) ) // Lock all buffer
        {
          memcpy( vPool, ptr, size <= Allign.Byte_Size ? size : Allign.Byte_Size );
        }
        else
        {
          return false;
        }

        if( D3D_OK != pIDirect3DBuffer->Unlock() ) // Unlock all buffer
        {
          return false;
        }
      
        return true;
      }
      virtual bool  GetData( void* ptr, UINT64 size )
      { 
        void* vPool = (void*) 0;  

        if( D3D_OK == pIDirect3DBuffer->Lock(0, 0, &vPool, D3DLOCK_NOSYSLOCK ) ) // Lock all buffer
        {
          memcpy( ptr, vPool, size <= Allign.Byte_Size ? size : Allign.Byte_Size );
        }
        else
        {
          return false;
        }

        if( D3D_OK != pIDirect3DBuffer->Unlock() ) // Unlock all buffer
        {
          return false;
        }
      
        return true;
      }    
      virtual Buffer::Descriptor GetDescriptor() { return Descriptor; }
      virtual bool Prepare(UINT64 parameter_id, Buffer::Allign *pallign = NULL )
      {
        pRender->IDirect3DPreparedIndexBufferAllign = pallign ? pallign : &(this->Allign);
        if(pRender->IDirect3DPreparedIndexBuffer != pIDirect3DBuffer)
        {
          pRender->IDirect3DPreparedIndexBuffer = pIDirect3DBuffer;
          //pRender->PreparedIndexBuffer = this;
          if( D3D_OK == pIDirect3DDevice->SetIndices( pIDirect3DBuffer ) )
          {
             return true;
          }
          return false;
        }
        return true;
      }
      virtual bool Map(Mapping::Descriptor *pDescriptor)
      {
        if( D3D_OK == pIDirect3DBuffer->Lock(0,0,&(pDescriptor->pBuffer),NULL) )
        {
           pDescriptor->Size = Allign.Byte_Size;
           MapStatus = Mapping::MS_Mapped;
           return true;
        }
        else
        {
           pDescriptor->pBuffer = NULL;
           pDescriptor->Size    = 0;
           return false;
        }
      };
      virtual bool Unmap()
      {
        if( D3D_OK == pIDirect3DBuffer->Unlock() )
        {
           MapStatus = Mapping::MS_Unmapped;
           return true;
        }
        else
        {
           return false;
        }
      };
      virtual Mapping::Status GetMappingStatus() { return MapStatus; };
      virtual Render::Resource::Type GetType() { return Render::Resource::RT_Buffer; }
      UINT64 GetPrimitiveCount( D3DPRIMITIVETYPE topology )
      {
        switch( topology )
        {
          case D3DPT_POINTLIST     : return Allign.Element_End;
          case D3DPT_LINELIST      : return Allign.Element_End / 2;
          case D3DPT_LINESTRIP     : return Allign.Element_End - 1;
          case D3DPT_TRIANGLELIST  : return Allign.Element_End / 3;
          case D3DPT_TRIANGLESTRIP : return Allign.Element_End - 2;
          case D3DPT_TRIANGLEFAN   : return Allign.Element_End - 2;
          default                  : return 0;
        }
      }
      UINT64 GetPrimitiveCount( Buffer::Topology topology )
      {
        switch( topology )
        {
          case Buffer::BT_Point_List     : return Allign.Element_End;
          case Buffer::BT_Line_List      : return Allign.Element_End / 2;
          case Buffer::BT_Line_Strip     : return Allign.Element_End - 1;
          case Buffer::BT_Triangle_List  : return Allign.Element_End / 3;
          case Buffer::BT_Triangle_Strip : return Allign.Element_End - 2;
          case Buffer::BT_Triangle_Fan   : return Allign.Element_End - 2;
          default                        : return 0;
        }
      }
      virtual IBuffer* CreateSubBuffer( UINT64 element_count )
      {
        if( IsSubBufferCreatable( element_count ) )
        {
          Buffer::Allign SubAllign;
          Allign.CreateSubAllign( SubBufferOffsetInElements, element_count, SubAllign);
          SubBuffers.push_back( new SubBuffer(this, SubAllign) );
          return SubBuffers[SubBuffers.size()-1];
        }
        return NULL;
      }
      virtual vector<IBuffer*>* GetSubBuffers()
      {
        return &SubBuffers;
      }         
      ~BufferDX9Index()
      {
        SAFE_RELEASE(pIDirect3DBuffer);
      }
    public:
      CRenderDX9         *pRender;
      Buffer::Descriptor  Descriptor;
      Buffer::Allign      Allign;
      Layout::Declaration       Declaration;

      Mapping::Status     MapStatus;

      IDirect3DDevice9      *pIDirect3DDevice; 
      IDirect3DIndexBuffer9 *pIDirect3DBuffer;

      // Subbuffers:
      UINT64 SubBufferOffsetInElements;
      vector<IBuffer*> SubBuffers;
    };
    class BufferDX9Vertex : public IBufferDX9
    {
    public:
      BufferDX9Vertex(CRenderDX9 *prender, Layout::Declaration &input, UINT64 elementCount)
      {
        pRender = prender;
        pIDirect3DDevice  = pRender->pIDevice;

        Allign.Innitialize(0, &input, elementCount);
        
        SubBufferOffsetInElements = NULL;

        if( D3D_OK == prender->pIDevice->CreateVertexBuffer( Allign.Byte_Size,
                                                            0,                                        
                                                            0,                           
                                                            D3DPOOL_MANAGED,                          
                                                            &pIDirect3DBuffer,
                                                            0 ) )
          {
            // OK
            Descriptor.pAllign = &Allign;
            Declaration = input;
            Descriptor.pInput  = &Declaration;
          }
          else
          {
            // NOK
            Descriptor.pAllign = NULL;
            Descriptor.pInput  = NULL;
          }    
      }
      virtual bool  SetData( void* ptr, UINT64 size ) 
      { 
        void* vPool = (void*) 0;

        if( D3D_OK == pIDirect3DBuffer->Lock(0, 0, &vPool, D3DLOCK_NOSYSLOCK ) ) // Lock all buffer
        {
          memcpy( vPool, ptr, size <= Allign.Byte_Size ? size : Allign.Byte_Size );
        }
        else
        {
          return false;
        }

        if( D3D_OK != pIDirect3DBuffer->Unlock() ) // Unlock all buffer
        {
          return false;
        }
      
        return true;
      }
      virtual bool  GetData( void* ptr, UINT64 size )
      { 
        void* vPool = (void*) 0;  

        if( D3D_OK == pIDirect3DBuffer->Lock(0, 0, &vPool, D3DLOCK_NOSYSLOCK ) ) // Lock all buffer
        {
          memcpy( ptr, vPool, size <= Allign.Byte_Size ? size : Allign.Byte_Size );
        }
        else
        {
          return false;
        }

        if( D3D_OK != pIDirect3DBuffer->Unlock() ) // Unlock all buffer
        {
          return false;
        }
      
        return true;
      }    
      virtual Buffer::Descriptor GetDescriptor() { return Descriptor; }
      virtual bool Prepare( UINT64 parameter_id, Buffer::Allign *pallign = NULL )
      {
        /*
        if( pRender->IDirect3DPreparedVertexBuffers[parameter_id] == pIDirect3DBuffer )
        {
          if( pallign )
          {
            if( pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id] )
            {
              if( pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id]->Byte_Begin          == pallign->Byte_Begin &&
                  pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id]->Single_Element_Size == pallign->Single_Element_Size  )
              {
                return true;
              }
            }
            else
            {
              pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id] = pallign;
            }
          }
          else
          {
            if( pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id] )
            {
              if( pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id]->Byte_Begin          == pallign->Byte_Begin &&
                  pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id]->Single_Element_Size == pallign->Single_Element_Size  )
              {
                return true;
              }
            }
            else
            {
              pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id] = &(this->Allign);
            }
          }
        }
        */
        //if(pRender->IDirect3DPreparedVertexBuffers[parameter_id] != pIDirect3DBuffer)
        {
          pRender->IDirect3DPreparedVertexBuffers[parameter_id] = pIDirect3DBuffer;
          
          pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id] = pallign ? pallign : &(this->Allign);
          
          UINT64 Byte_Begin          = pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id]->Byte_Begin;
          UINT64 Single_Element_Size = pRender->IDirect3DPreparedVertexBuffersAllign[parameter_id]->Single_Element_Size;
          if( D3D_OK != pIDirect3DDevice->SetStreamSource( parameter_id, pIDirect3DBuffer, Byte_Begin, Single_Element_Size ) )
          {
            return false;
          }
          return true;
        }
        return true;
      } 
      virtual bool Map( Mapping::Descriptor *pDescriptor )
      {
        if( D3D_OK == pIDirect3DBuffer->Lock(0,0,&(pDescriptor->pBuffer),NULL) )
        {
          pDescriptor->Size = Allign.Byte_Size;
          MapStatus = Mapping::MS_Mapped;
          return true;
        }
        else
        {
          pDescriptor->pBuffer = NULL;
          pDescriptor->Size    = 0;
          return false;
        }
      }    
      virtual bool Unmap( )
      {
        if( D3D_OK == pIDirect3DBuffer->Unlock() )
        {
           MapStatus = Mapping::MS_Unmapped;
           return true;
        }
        else
        {
           return false;
        }
      }
      virtual Mapping::Status GetMappingStatus() { return MapStatus; };      
      virtual Render::Resource::Type GetType() { return Render::Resource::RT_Buffer; }    
      UINT64 GetPrimitiveCount( D3DPRIMITIVETYPE topology )
      {
        switch( topology )
        {
          case D3DPT_POINTLIST     : return Allign.Element_End;
          case D3DPT_LINELIST      : return Allign.Element_End / 2;
          case D3DPT_LINESTRIP     : return Allign.Element_End - 1;
          case D3DPT_TRIANGLELIST  : return Allign.Element_End / 3;
          case D3DPT_TRIANGLESTRIP : return Allign.Element_End - 2;
          case D3DPT_TRIANGLEFAN   : return Allign.Element_End - 2;
          default                  : return 0;
        }
      }  
      UINT64 GetPrimitiveCount( Buffer::Topology topology )
      {
        switch( topology )
        {
          case Buffer::BT_Point_List     : return Allign.Element_End;
          case Buffer::BT_Line_List      : return Allign.Element_End / 2;
          case Buffer::BT_Line_Strip     : return Allign.Element_End - 1;
          case Buffer::BT_Triangle_List  : return Allign.Element_End / 3;
          case Buffer::BT_Triangle_Strip : return Allign.Element_End - 2;
          case Buffer::BT_Triangle_Fan   : return Allign.Element_End - 2;
          default                : return 0;
        }
      };    
      virtual IBuffer* CreateSubBuffer( UINT64 element_count )
      {
        if( IsSubBufferCreatable( element_count ) )
        {
          Buffer::Allign SubAllign;
          Allign.CreateSubAllign( SubBufferOffsetInElements, element_count, SubAllign);
          SubBuffers.push_back( new SubBuffer(this, SubAllign) );
          return SubBuffers[SubBuffers.size()-1];
        }
        return NULL;
      }
      virtual vector<IBuffer*>* GetSubBuffers()
      {
        return &SubBuffers;
      }      
      ~BufferDX9Vertex( )
      {
        SAFE_RELEASE(pIDirect3DBuffer);
      }
    public:
      CRenderDX9         *pRender;
      Buffer::Descriptor  Descriptor;
      Buffer::Allign      Allign;
      Layout::Declaration       Declaration;

      Mapping::Status     MapStatus;

      IDirect3DDevice9       *pIDirect3DDevice; 
      IDirect3DVertexBuffer9 *pIDirect3DBuffer;

      // Subbuffers:
      UINT64 SubBufferOffsetInElements;
      vector<IBuffer*> SubBuffers;
    };
    class TextureDX9 : public ITexture, public IInternalMappable
    {
    private:
      D3DFORMAT GetTextureFormat( Layout::Declaration *pinput )
      {
        assert( pinput->IsValid() && Violations::Value_not_valid );
        
        return ConvertTextureFormat( (*pinput->GetDescriptor())[0].Format );
      };
      Layout::Format ConvertTextureFormat( D3DFORMAT format )
      {
        switch( format )
        {
        case D3DFMT_A8R8G8B8 : return Layout::LF_A8R8G8B8;
        case D3DFMT_L8       : return Layout::LF_L8;
        case D3DFMT_L16      : return Layout::LF_L16;
        //..
        default : return Layout::LF_Unknown;
        }
      };
      D3DFORMAT ConvertTextureFormat( Layout::Format format )
      {
        switch( format )
        {
        case Layout::LF_A8R8G8B8 : return D3DFMT_A8R8G8B8;
        case Layout::LF_L8       : return D3DFMT_L8;
        case Layout::LF_L16      : return D3DFMT_L16;
        //..
        default : return D3DFMT_UNKNOWN;
        }
      };
    public:
      TextureDX9( CRenderDX9 *prender, wstring name )
      {
        tdesc.pDimmension = &vDimmension;
        tdesc.pInput      = &vInput;
        tdesc.pAllign     = &vAllign;
        // Read the D3DX Texture from file
        // wstring wstr = StringToWString(string(p_str));
        MapStatus = Mapping::MS_Unmapped;
        pRender = prender;
        pIDirect3DDevice = prender->pIDevice;

        pITextureSysMem = 0;

        WCHAR str[MAX_PATH];
        if( D3D_OK != DXUTFindDXSDKMediaFileCch( str, MAX_PATH, name.c_str() ) )
        {
          // NOT OK
        }

        if( D3D_OK == D3DXCreateTextureFromFileEx( prender->pIDevice, str, D3DX_DEFAULT, D3DX_DEFAULT, 
                                         D3DX_DEFAULT, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                         D3DX_FILTER_TRIANGLE, D3DX_FILTER_TRIANGLE, 0, 
                                         NULL, NULL, &pITexture ) )
        {
          // OK
          D3DSURFACE_DESC surf;
          pITexture->GetLevelDesc(0, &surf);
          
          tdesc.pInput->AddFormat( ConvertTextureFormat( surf.Format ) ); 
          
          tdesc.pDimmension->SetDimmensionLength( Layout::LD_U, surf.Height );
          tdesc.pDimmension->SetDimmensionLength( Layout::LD_V, surf.Width  );

          tdesc.pAllign->Single_Element_Size =  tdesc.pInput->GetElementSize();
          tdesc.pAllign->Byte_Size = tdesc.pAllign->Single_Element_Size * surf.Height * surf.Width;
        }
        else
        {
          // NOT OK
          tdesc.pInput->AddFormat( Layout::LF_Unknown ); 
          
          tdesc.pDimmension->SetDimmensionLength( Layout::LD_U, 0 );
          tdesc.pDimmension->SetDimmensionLength( Layout::LD_V, 0 );

          tdesc.pAllign->Single_Element_Size = 0;
          tdesc.pAllign->Byte_Size           = 0;
        }
      }
      TextureDX9( CRenderDX9 *prender, Layout::Declaration *pinput, Layout::Dimmension *pdimmension )
      {
        pRender = prender;
        pIDirect3DDevice = pRender->pIDevice;

        vDimmension = *pdimmension;
        vInput      = *pinput;
        
        tdesc.pDimmension = &vDimmension;
        tdesc.pInput      = &vInput;
        tdesc.pAllign     = &vAllign;
        
        pITextureSysMem = 0;

        if( D3D_OK == D3DXCreateTexture( pRender->pIDevice, vDimmension.GetDimmensionLength(Layout::LD_U),
                                                  vDimmension.GetDimmensionLength(Layout::LD_V),
                                                  1,
                                                  0,
                                                  GetTextureFormat(tdesc.pInput),
                                                  D3DPOOL_DEFAULT,
                                                  &pITexture ) )
        {
          // OK
          tdesc.pAllign->Single_Element_Size =  tdesc.pInput->GetElementSize();
          tdesc.pAllign->Byte_Size = tdesc.pAllign->Single_Element_Size * pITexture->Height * pITexture->Width;

          //Create System memory texture to write data to D3DPOOL_DEFAULT texture:

          if( D3D_OK == D3DXCreateTexture( pRender->pIDevice, vDimmension.GetDimmensionLength(Layout::LD_U),
                                                    vDimmension.GetDimmensionLength(Layout::LD_V),
                                                    1,
                                                    0,
                                                    GetTextureFormat(tdesc.pInput),
                                                    D3DPOOL_SYSTEMMEM,
                                                    &pITextureSysMem ) )
          {
            int i = 0;
          }
          
        }
        else
        {
          tdesc.pInput->Reset(); 
          tdesc.pDimmension->Reset();

          tdesc.pAllign->Single_Element_Size = 0;
          tdesc.pAllign->Byte_Size           = 0;
        }
      }
      virtual bool SetLod( UINT64 lod ) { /* tdesc.LOD = lod; */ return true; };
      virtual bool SetData( void* ptr, UINT64 size ) { return true; }
      virtual bool GetData( void* ptr, UINT64 size ) { return true; }
      virtual bool Map( Mapping::Descriptor *pDescriptor )
      {
  	    if( pITextureSysMem )
        {
          D3DSURFACE_DESC surf;
          pITextureSysMem->GetLevelDesc(0, &surf);
          
          D3DLOCKED_RECT rect;
          if( D3D_OK == pITextureSysMem->LockRect(0, &rect, NULL, 0) )
          {
            pDescriptor->pBuffer = rect.pBits;
            pDescriptor->Size    = rect.Pitch*surf.Height;
            MapStatus = Mapping::MS_Mapped;
            return true;
          }
          else
          {
            pDescriptor->pBuffer = NULL;
            pDescriptor->Size    = 0;
            return false;
          }
        }
        else
        {
          D3DSURFACE_DESC surf;
          pITexture->GetLevelDesc(0, &surf);
          
          D3DLOCKED_RECT rect;
          if( D3D_OK == pITexture->LockRect(0, &rect, NULL, 0) )
          {
            pDescriptor->pBuffer = rect.pBits;
            pDescriptor->Size    = rect.Pitch*surf.Height;
            MapStatus = Mapping::MS_Mapped;
            return true;
          }
          else
          {
            pDescriptor->pBuffer = NULL;
            pDescriptor->Size    = 0;
            return false;
          }
        }
      }
      virtual bool Unmap()
      {
  	    if( pITextureSysMem )
        {          
          if( D3D_OK == pITextureSysMem->UnlockRect(0) )
          {
            //pITexture->UnlockRect(0);

            if( D3DERR_INVALIDCALL == pIDirect3DDevice->UpdateTexture(pITextureSysMem,pITexture) )
            {
              int i = 0;
            }
            
            MapStatus = Mapping::MS_Unmapped;

            return true;
          }
          //else
          {
             return false;
          }
        }
        else
        {
          if( D3D_OK == pITexture->UnlockRect(0) )
          {
            MapStatus = Mapping::MS_Unmapped;

            return true;
          }
          else
          {
             return false;
          }
        }
      }       
      virtual Mapping::Status GetMappingStatus() { return MapStatus; };      
      virtual Texture::Desctiptor GetDescriptor()
      {
        return tdesc;
      }
      virtual Render::Resource::Type GetType() { return Render::Resource::RT_Texture; }
      ~TextureDX9()
      {
        SAFE_RELEASE(pITexture);
      }
    public:
      CRenderDX9       *pRender;
      IDirect3DDevice9 *pIDirect3DDevice; 

      Mapping::Status MapStatus;

      IDirect3DTexture9 *pITexture;
      IDirect3DTexture9 *pITextureSysMem;
      Texture::Desctiptor tdesc;

      Layout::Declaration vInput;
      Layout::Dimmension vDimmension;
      Texture::Allign    vAllign;

    };
    class EffectDX9 : public IEffect // IDirect3DDevice9::SetVertexShaderConstantB; D3DXCompileShaderFromFile; ID3DXConstantTable::GetConstantDesc; D3DXCONSTANT_DESC;
    {                                // ID3DXBaseEffect::GetPixelShader; D3DXGetShaderConstantTable;
    public:
      EffectDX9(CRenderDX9 *prender, wstring name)
      {
        Technique_ID = (UINT64) -1;
        Pass_ID      = (UINT64) -1;
              
        WCHAR str[MAX_PATH];
        if( D3D_OK !=  DXUTFindDXSDKMediaFileCch( str, MAX_PATH, name.c_str() ) )
        {
          // NOT OK
        }

        if( D3D_OK == D3DXCreateEffectFromFile( prender->pIDevice, str, NULL, NULL, D3DXSHADER_NO_PRESHADER, NULL, &pIEffect, NULL ) )
        {
          // OK
        }
        else
        {
          // NOT OK
        }

        D3DXEFFECT_DESC EffectDesc;

        pIEffect->GetDesc(&EffectDesc);        
        
        //Prepare parameters information:
        for( UINT64 i = 0; i<EffectDesc.Parameters; i++ )
        {
          D3DXHANDLE parameter = pIEffect->GetParameter( NULL, i );
          if( parameter )
          {
            D3DXPARAMETER_DESC param_desc;
            pIEffect->GetParameterDesc(parameter, &param_desc);
            
            Effect::ParameterDescriptor gr_param_desc;
            
            gr_param_desc.Name = param_desc.Name;
            gr_param_desc.Id   = i;
            gr_param_desc.Type = (UINT64) param_desc.Type;
            
            Parameters.push_back( gr_param_desc );
            
            Parameter_Handles.push_back( parameter );
          }
          else
          {
            break;
          }
        }
    
        //Prepare technique information:
        for( UINT64 i = 0; i < EffectDesc.Techniques; i++ )
        {
          D3DXHANDLE technique = pIEffect->GetTechnique(i);
          if( technique )
          {
            D3DXTECHNIQUE_DESC technique_desc;
            if( D3D_OK == pIEffect->GetTechniqueDesc( technique, &technique_desc) )
            {
              Effect::TechniqueDescriptor gr_technique_desc;
              
              gr_technique_desc.Name   = technique_desc.Name;
              gr_technique_desc.Id     = i;
              gr_technique_desc.Passes = technique_desc.Passes;
              
              Techniques.push_back(gr_technique_desc);
              
              Technique_Handles.push_back(technique);             
            }
            else
            {
              break;        
            }
          }
          else
          {
            break;
          }
        }

        // NEED TESTING:
        /*
        D3DXEFFECT_DESC EffectDesc;
        pIEffect->GetDesc(&EffectDesc); 
        //Prepare function information:
        for( UINT64 i = 0; i<EffectDesc.Functions; i++ )
        {
          D3DXHANDLE function = pIEffect->GetFunction( i );
          if( function )
          {
            IDirect3DVertrexShader9* pIVertexShader = NULL;
            IDirect3DPixelShader9*   pIPixelShader  = NULL;
            if( D3D_OK == pIEffect->GetVertexShader(function,&pIVertexShader) )
            {
              UINT size;
              pIVertexShader->GetFunction(NULL, &size);
              void* pData = new INT64[size];
              pIVertexShader->GetFunction(pData, &size);

              ID3DXConstantTable *pShaderConstantTable = NULL;
              D3DXGetShaderConstantTable( (CONST INT64*) pShader, &pShaderConstantTable );
              
              UINT ConstandIndex = 0;
              D3DXCONSTANT_DESC ConstDesc;
              pShaderConstantTable->GetConstantDesc( pShaderConstantTable->GetConstant(NULL,ConstandIndex), &ConstDesc, &size );

              UINT REGISTER = ConstDesc.RegisterIndex; // Used to set by - IDirect3DDevice9::SetVertexShaderConstantB(); 

              delete pData;
            }
            else if( D3D_OK == pIEffect->GetPixelShader(function,&pIPixelShader) )
            {

            }
          }
          else
          {
            break;
          }
        }
        */
      }
      virtual bool SetParameter(UINT64 parameter_id, ITexture *parameter)
      {
         if( D3D_OK == pIEffect->SetTexture(Parameter_Handles[parameter_id], ((TextureDX9*)parameter)->pITexture ) )
         {
           return true;
         }
         else
         {
           return false;       
         }
      }
      virtual bool SetParameter(UINT64 parameter_id, float *parameter)
      {
        if( D3D_OK == pIEffect->SetFloat( Parameter_Handles[parameter_id], *parameter ) )
        {
          return true;
        }
        else
        {
          return false;       
        }
      }
      virtual bool SetParameter(UINT64 parameter_id, vector<float> *parameter)
      {
        if( D3D_OK == pIEffect->SetFloatArray(Parameter_Handles[parameter_id], &(*parameter)[0], parameter->size() ) )
         {
           return true;
         }
         else
         {
           return false;       
         }
      }
      virtual bool SetParameter(UINT64 parameter_id, D3DXMATRIXA16 *parameter)
      {
         if( D3D_OK == pIEffect->SetMatrix(Parameter_Handles[parameter_id], parameter ) )
         {
           return true;
         }
         else
         {
           return false;       
         }
      }
      virtual bool SetTechnique(UINT64 technique_id)
      {
        if( D3D_OK == pIEffect->SetTechnique( Technique_Handles[technique_id] ) )
        {
          return true;
        }
        else
        {
          return false;
        }
      }
      
      virtual Render::Resource::Type GetType() { return Render::Resource::RT_Effect; }

      virtual bool BeginTechnique( UINT64 technique_id )
      {
        if( technique_id < Technique_Handles.size() )
        {
          if( D3D_OK != pIEffect->SetTechnique( Technique_Handles[technique_id] ) ) //@todo: shood optimise it by D3DXHANDLE
          {
            return false;
          }

          UINT passes_count;
          if( D3D_OK != pIEffect->Begin( &passes_count, 0 ) )
          {
            return false;
          }
          
          Technique_ID = technique_id;

          return true;
        }
        else
        {
          return false;
        }
      }
      virtual bool EndTechnique()
      {
        if( D3D_OK == pIEffect->End() )
        {
          return true;
        }
        else
        {
          return false;
        }
      }
      virtual bool BeginPass( UINT64 pass_id )
      {
        if( pass_id < Techniques[Technique_ID].Passes )
        {
          if( D3D_OK == pIEffect->BeginPass( pass_id ) )
          {
            return true;
          }
        }
        return false;
      }
      virtual bool EndPass()
      {
        if( D3D_OK == pIEffect->EndPass( ) )
        {
          return true;
        }
        return false;
      }

      virtual vector<Effect::ParameterDescriptor>* GetParameters() { return &Parameters; }
      virtual vector<Effect::TechniqueDescriptor>* GetTechniques() { return &Techniques; }
      ~EffectDX9()
      {
        SAFE_RELEASE(pIEffect);
      }
    public:
      ID3DXEffect *pIEffect;

      UINT64 Technique_ID;
      UINT64 Pass_ID;

      vector <Effect::ParameterDescriptor> Parameters;
      vector <D3DXHANDLE> Parameter_Handles;
      vector <Effect::TechniqueDescriptor> Techniques;
      vector <D3DXHANDLE> Technique_Handles;
    };
    class LayoutDX9 : public ILayout
    {
    public:
      LPDIRECT3DVERTEXDECLARATION9 pIDirect3DVertexDeclaretion;
      vector<D3DVERTEXELEMENT9> VertexElements;
      Layout::Descriptor Descriptor;
    public:  
      LayoutDX9( CRenderDX9 *prender, Layout::Descriptor &descriptor )
      {
        Descriptor = descriptor;

        UINT64 Index  = 0;
        UINT64 Offset = 0;
        D3DVERTEXELEMENT9 ve; 

        vector<Layout::Declaration>* pInputs = descriptor.GetInputs();
        for( UINT64 i = 0; i < pInputs->size(); i++ )
        {
          vector<Layout::Declaration::Descriptor>* pDescriptor = pInputs->at(i).GetDescriptor();
          for( UINT64 j = 0; j < pDescriptor->size(); j++ )
          {

            ve.Stream = i;
            ve.Offset = Offset;
            ve.Type   = GetDX9Format(pDescriptor->at(j).Format);
            ve.Method = D3DDECLMETHOD_DEFAULT;
            ve.Usage  = GetDX9Usage(pDescriptor->at(j).Usage);
            if( j == 0 )
            {
              ve.UsageIndex = 0;
            }
            else if( pDescriptor->at(j).Usage == pDescriptor->at(j-1).Usage )
            {
              ve.UsageIndex = VertexElements[Index-1].UsageIndex + 1;
            }
            else
            {
              ve.UsageIndex = 0;
            }

            Offset += Layout::GetSize(pDescriptor->at(j).Format);
            
            VertexElements.push_back(ve);
          }
        }

        ve.Stream = 0xFF;
        ve.Offset = 0;
        ve.Type   = D3DDECLTYPE_UNUSED;
        ve.Method = 0;
        ve.Usage  = 0;
        ve.UsageIndex = 0;

        VertexElements.push_back(ve);

        if( D3D_OK != prender->pIDevice->CreateVertexDeclaration((CONST D3DVERTEXELEMENT9*)&(VertexElements[0]),&pIDirect3DVertexDeclaretion) )
        {
          pIDirect3DVertexDeclaretion = 0;
        }
        
        VertexElements.resize(0);
      }
      virtual Layout::Descriptor* GetDescriptor()
      {
        return &Descriptor;
      }
      virtual Render::Resource::Type GetType() { return Render::Resource::RT_Layout; }
    private:
      D3DDECLTYPE GetDX9Format(Layout::Format format)
      {
        static D3DDECLTYPE DeclFormat[] = 
        {
          /* LF_Unknown  */ D3DDECLTYPE_UNUSED,
          /* LF_Float3   */ D3DDECLTYPE_FLOAT3,
          /* LF_Float2   */ D3DDECLTYPE_FLOAT2,
          /* LF_DWord    */ D3DDECLTYPE_UNUSED,      
          /* LF_Word     */ D3DDECLTYPE_UNUSED, 
          /* LF_A8R8G8B8 */ D3DDECLTYPE_UNUSED,
          /* LF_L8       */ D3DDECLTYPE_UNUSED,
          /* LF_L16      */ D3DDECLTYPE_UNUSED,
        };
        return DeclFormat[(int)format];
      }
      D3DDECLUSAGE GetDX9Usage(Layout::Usage usage)
      {
        static D3DDECLUSAGE DeclUsage[] = 
        {      
          /* LU_Unknown     */ (D3DDECLUSAGE) 100,
          /* LU_Position    */ D3DDECLUSAGE_POSITION,
          /* LU_Normal      */ D3DDECLUSAGE_NORMAL,
          /* LU_Tangent     */ D3DDECLUSAGE_TANGENT,
          /* LU_Binormal    */ D3DDECLUSAGE_BINORMAL,
          /* LU_Texcoord    */ D3DDECLUSAGE_TEXCOORD,
          /* LU_IndexDouble */ (D3DDECLUSAGE) 100,      
          /* LU_Index       */ (D3DDECLUSAGE) 100,
        };
        return DeclUsage[(int)usage];
      }
    };
  public:
    CRenderDX9(IDirect3DDevice9* device)
    {
      pIDevice = device;
      IDirect3DTopology = D3DPT_FORCE_DWORD;

      // Buffer prepare optimization:
      IDirect3DPreparedIndexBuffer = 0;
      IDirect3DPreparedIndexBufferAllign = 0;
      for( int i=0; i<16; i++ ) 
      { 
        IDirect3DPreparedVertexBuffers.push_back(0);
        IDirect3DPreparedVertexBuffersAllign.push_back(0);
      }

    }
    //
    virtual bool CreateResource(Render::Resource::Descriptor *pDescriptor, Render::IResource **ppResource)
    {
      switch( pDescriptor->Type )
      {
      case Rendering::Render::Resource::RT_Buffer:
        return *ppResource = CreateBuffer ( *pDescriptor->pInput, pDescriptor->Dimmension[0] );
      case Rendering::Render::Resource::RT_Texture:
        return *ppResource = CreateTexture( *pDescriptor->pInput, pDescriptor->Dimmension );
      case Rendering::Render::Resource::RT_SubBuffer:
        return *ppResource = CreateSubBuffer( dynamic_cast<Rendering::IBuffer*>(pDescriptor->pMasterResource), pDescriptor->Dimmension[0] );
      default:
        return false;
      }
    };
    virtual bool CreateResource( Render::IResourceConfigurator *configurator, Render::IResource **ppResource )
    {
      Render::BufferConfigurator*  buf_conf = 0;
      Render::TextureConfigurator* tex_conf = 0;
      if( buf_conf = dynamic_cast<Render::BufferConfigurator*>(configurator) )
      {
        if( buf_conf->pParentBuffer )
        { // Sub-Buffer:
          return *ppResource = CreateSubBuffer( buf_conf->pParentBuffer, buf_conf->dimmension[0] );
        }
        else
        { // Buffer:
          return *ppResource = CreateBuffer ( buf_conf->input, buf_conf->dimmension[0] );
        }
      }
      else if( tex_conf = dynamic_cast<Render::TextureConfigurator*>(configurator) )
      { // Texture:
        return *ppResource = CreateTexture( tex_conf->input, tex_conf->dimmension );
      }
      return false;
    };
    virtual bool DestroyResource(Rendering::Render::IResource **ppResource)
    {
      delete *ppResource;
      *ppResource = 0;
      return true;
    };
    //
    virtual IBuffer*   CreateBuffer    ( Layout::Declaration &input, UINT64 elementCount )
    {
      switch( input.GetDescriptor()->at(0).Format )
      {
        case Layout::LF_DWord:
        case Layout::LF_Word:
          return (IBuffer*) new BufferDX9Index(this,input,elementCount);
        case Layout::LF_Float3:
        case Layout::LF_Float2:
          return (IBuffer*) new BufferDX9Vertex(this,input,elementCount);
        default:
          return NULL;
      }
    }
    virtual IBuffer*   CreateSubBuffer ( Rendering::IBuffer *pmaster, UINT64 elementCount )
    {
      if( pmaster )
      {
        IBufferDX9* pBuffer = (IBufferDX9*) pmaster;
        return pBuffer->CreateSubBuffer(elementCount);
      }
      return NULL;
    };
    virtual ITexture*  CreateTexture   ( Layout::Declaration &input, Layout::Dimmension &dimmension )
    {
      return new TextureDX9( this, &input, &dimmension );
    }
    //
    virtual ITexture*  CreateTexture ( wstring filename )
    {
      return new TextureDX9(this, filename);
    }
    virtual IEffect*   CreateEffect  ( wstring filename )
    {
      return new EffectDX9(this, filename);
    }
    virtual ILayout*   CreateLayout  ( Layout::Descriptor &descriptor )
    {
      return new LayoutDX9(this,descriptor);
    }
    //
    virtual bool Map( Mapping::IMappable* pcresource, Mapping::Descriptor *pDescriptor )
    {
      IInternalMappable* pim = dynamic_cast<IInternalMappable*>(pcresource);
      if(pim)
      {
        return pim->Map(pDescriptor);
      }
      return false;
    }
    virtual bool Unmap( Mapping::IMappable* pcresource )
    {
      IInternalMappable* pim = dynamic_cast<IInternalMappable*>(pcresource);
      if(pim)
      {
        return pim->Unmap();
      }
      return false;
    }
    //
    virtual bool Clear ( )
    {
      if( D3D_OK == pIDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(111, 148, 204), 1.0f, 0L ) )
      {
        //pIDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME ); // D3DFILL_WIREFRAME D3DFILL_SOLID
        //pIDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE  );
        return true;
      }
      else
      {
        return false;
      }
    };
    virtual bool Begin ( ) 
    { 
      if( D3D_OK == pIDevice->BeginScene() ) 
      {
        return true;
      }
      else
      {
        return false;
      }
    }
    virtual bool SetBuffer( UINT64 parameter_id , IBuffer* parameter)
    {
      return ((IBufferDX9*)parameter)->Prepare(parameter_id);
    };
    virtual bool SetLayout( ILayout *layout )
    {
      if( D3D_OK == pIDevice->SetVertexDeclaration(((LayoutDX9*)layout)->pIDirect3DVertexDeclaretion) )
      {
        return true;
      }
      else
      {
        return false;
      }
    }
    virtual bool SetTopology( Buffer::Topology topology ) { IDirect3DTopology = GetTopology(topology); return true; }
    virtual bool DrawVertexes()
    {
      if( D3D_OK == pIDevice->DrawPrimitive( IDirect3DTopology, 
                                             IDirect3DPreparedVertexBuffersAllign[0]->Element_Begin , 
                                             GetPrimitiveCount( IDirect3DTopology, IDirect3DPreparedVertexBuffersAllign[0] ) ) )
      {
        return true;
      }
      return false;
    }
    virtual bool DrawIndexes()
    {
      if( D3D_OK == pIDevice->DrawIndexedPrimitive( IDirect3DTopology, 
                                                    IDirect3DPreparedVertexBuffersAllign[0]->Element_Begin, //BufferLogic.Format_To_SystemBuffer[Buffer0->Format]->SubBuffer[Buffer0->LocalId].Align.Element_Begin,
                                                    0,
                                                    IDirect3DPreparedVertexBuffersAllign[0]->Element_Size,//BufferLogic.Format_To_SystemBuffer[Buffer0->Format]->SubBuffer[Buffer0->LocalId].Align.Element_Size, 
                                                    IDirect3DPreparedIndexBufferAllign->Element_Begin,  
                                                    GetPrimitiveCount( IDirect3DTopology, IDirect3DPreparedIndexBufferAllign ) ) ) 
      {
        return true;
      } 
      return false;
    }
    virtual bool Finish ( ) 
    { 
      if( D3D_OK != pIDevice->EndScene() )
      {
        return false;
      }

      if( D3D_OK == pIDevice->Present( NULL, NULL, NULL, NULL ) )
      {
        return true;
      }
      else
      {
        return false;
      }
    }
    //
    ~CRenderDX9() { };
  };

}

#endif /* CRENDERDX9 */